package com.crazy.util.lock.wait;

import java.util.LinkedList;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;

/**
 * @author panyonghui
 * @describe 同一个锁支持创建多个Condition
 * @date 2022/1/13 14:36
 */
public class BlockingQueueDemo<E> {
    // 可消费数
    private int num = 0;
    // 物品队列
    LinkedList<E> linkedList = new LinkedList();

    // ReentrantLock锁
    ReentrantLock lock = new ReentrantLock();
    // 队列空时等待
    Condition empty = lock.newCondition();
    // 队列满时等待
    Condition full = lock.newCondition();

    public BlockingQueueDemo(int num){
        this.num = num;
    }

    /**
     * @describe 生产数据
     * @auther crazy_cat
     * @date 2022/1/13 14:45
     * @param
     * @return void
     */
    public void enqueue(E i){
        lock.lock();
        try{
            String threadName = Thread.currentThread().getName();
            while(linkedList.size() == num){
                System.out.println(threadName + "队列已满，停止生产");
                full.await();
            }
            // 生产数据
            linkedList.add(i);
            System.out.println(threadName + "生产后通知消费");
            empty.signal();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }

    /**
     * @describe 消费数据
     * @auther crazy_cat
     * @date 2022/1/13 14:46
     * @param
     * @return void
     * @modify
     */
    public E dequeue(){
        E n = null;
        lock.lock();
        try{
            String threadName = Thread.currentThread().getName();
            while (linkedList.size() == 0){
                System.out.println(threadName + "队列为空，停止消费");
                empty.await();
            }
            // 出队
            n = linkedList.removeFirst();
            System.out.println(threadName + "出队:" + n);
            System.out.println(threadName + "通知消费");
            full.signal();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
        return n;
    }

    public static void main(String[] args) {
        final BlockingQueueDemo<Integer> blockingQueueDemo = new BlockingQueueDemo<Integer>(2);
        for (int i = 0; i < 10; i++) {
            final int finalI = i;
            new Thread(new Runnable() {
                public void run() {
                    blockingQueueDemo.enqueue(finalI);
                }
            }).start();
        }

        for (int i = 0; i < 10; i++) {
            new Thread(new Runnable() {
                public void run() {
                    blockingQueueDemo.dequeue();
                }
            }).start();
        }
    }
}
